To upload the data, download the uber_pickups_lower_manhattan_wide_6h.csv and stick it in a folder and remember the directory.

# Install required libraries if necessary.
require("forecast")
require("lubridate")
Loading required package: lubridate

Attaching package: ‘lubridate’

The following object is masked from ‘package:base’:

    date
require("gridExtra")
require("tidyverse")
Loading required package: tidyverse
── Attaching packages ──────────────────────── tidyverse 1.2.1 ──
✔ tibble  2.1.3     ✔ purrr   0.3.2
✔ tidyr   1.0.0     ✔ dplyr   0.8.3
✔ readr   1.3.1     ✔ stringr 1.4.0
✔ tibble  2.1.3     ✔ forcats 0.4.0
── Conflicts ─────────────────────────── tidyverse_conflicts() ──
✖ lubridate::as.difftime() masks base::as.difftime()
✖ dplyr::combine()         masks gridExtra::combine()
✖ lubridate::date()        masks base::date()
✖ dplyr::filter()          masks timeSeries::filter(), stats::filter()
✖ lubridate::intersect()   masks base::intersect()
✖ dplyr::lag()             masks timeSeries::lag(), stats::lag()
✖ lubridate::setdiff()     masks base::setdiff()
✖ lubridate::union()       masks base::union()
require("caret")
Loading required package: caret
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

Attaching package: ‘caret’

The following object is masked from ‘package:purrr’:

    lift
require("tscount")
Loading required package: tscount
# Input data
uber_6h <- read_csv('../data/uber_pickups_lower_manhattan_wide_6h.csv') # Point this to the directory and file
Parsed with column specification:
cols(
  Pickup_date = col_datetime(format = ""),
  East_Village = col_double(),
  Gramercy = col_double(),
  GVillage_N = col_double(),
  GVillage_S = col_double(),
  Little_Italy = col_double(),
  LES = col_double(),
  SoHo = col_double(),
  Union_Sq = col_double()
)
uber_train <- uber_6h %>% filter(Pickup_date < ymd_hms("2015-06-01 00:00:00")) # This gives us a training set for all 8 locations
uber_test <- uber_6h %>% filter(Pickup_date >= ymd_hms("2015-06-01 00:00:00"))
uber_train <- uber_train %>% mutate(season=weekdays(Pickup_date, abbreviate=TRUE))
uber_test <- uber_test %>% mutate(season=weekdays(Pickup_date, abbreviate=TRUE))
# nrow(uber_6h) # if we want to check number of observations
# names(uber_6h) # use this command if you want to see the available time series

Train-test split

We will use the first five months as training data and predict Uber pickups for June 2015 in the East Village. The date is in positx format which can be parsed using the lubridate package.

Statistical forecasting techniques are used on the East Village training data, and will be evaluated on East Village test data.

# This block selects only east village data and splits it into train/test sets
full_ts <- msts(uber_6h$East_Village,
                    start=decimal_date(ymd_hms("2015-01-01 00:00:00")),
                    seasonal.periods=c(4, 1461))

train_ts <- msts(uber_train$East_Village,
                    start=decimal_date(ymd_hms("2015-01-01 00:00:00")),
                    seasonal.periods=c(4, 1461))

test_ts <- msts(uber_test$East_Village,
                    start=decimal_date(ymd_hms("2015-06-01 00:00:00")),
                    seasonal.periods=c(4, 1461))


#train_ts <- window(full_ts, start=decimal_date(ymd_hms("2015-01-01 00:00:00"))) # Train with obsercations up until june

#test_ts <- window(full_ts, start=decimal_date(ymd_hms("2015-06-01 00:00:00"))) # Test out predictions with June data
y_test <- as.numeric(test_ts)
test_size <- length(y_test)
# Show full data set
p1 <- full_ts %>% autoplot(series="All Trips") + 
  ggtitle("Full Dataset") +
  guides(colour=guide_legend("Data"))+
  scale_color_manual(values=c("black"))

# Show training dataset
p2 <- autoplot(train_ts, series="Training")+
  autolayer(test_ts,  series="Validation")+
  guides(colour=guide_legend("Split"))+
  scale_color_manual(values=c("black", "grey"))+
  ggtitle("Train/Test Split")

grid.arrange(p1, p2, nrow=2, ncol=1)

To look at any of the other 7 pickup locations, call lil_italy <- uber_train %>% select(Little_Italy) for example.

# Show subset of data to look for patterns

jan_to_march <- train_ts %>% window(end=decimal_date(ymd_hms("2015-03-01 00:00:00")))

jan_to_march %>% autoplot()+
  ggtitle("2 Months of Uber Pickup Data")

By aggregating to 6-hour windows we model the demand per “shift” in a day.

Moving Average Filter

This helps with telling the seasonality of the data. It looks like there is a spike in demand at the beginning of each month. In addition there are about three spikes during each month. This probably corresponds to weekly spikes in demand.

Analyzing the trend

lm(train_ts ~ time(train_ts)) %>% fitted() -> yhat.lm
autoplot(train_ts) + 
  geom_line(mapping=aes(x=time(train_ts), y=yhat.lm), color="red")+
  ggtitle("Linear Trend Fit")+
  xlab("Time")+
  ylab("Pickups")

There is a slight linear trend. Let’s see what the ACF and PACF plots look like.

p1 <- ggAcf(train_ts) + ggtitle("ACF of Uber Pickups")
p2 <- ggPacf(train_ts) + ggtitle("PACF of Uber Pickups")
grid.arrange(p1, p2, nrow=2, ncol=1)

Not very informative although it initially hints toward an AR(p) model after a difference.

Before checking the lag plot, the trend should be removed via differencing.

Differencing

# code for differencing the training data
train_diff <- diff(train_ts)
lm(train_diff ~ time(train_diff)) %>% fitted() -> yhat.lm

train_diff %>% autoplot() + 
  geom_line(mapping=aes(x=time(train_diff), y=yhat.lm), color="red") + 
  ggtitle("Differenced Pickup Data") + 
  ylab("Change in Pickups")

# Comparison of time series plots

lm(train_ts ~ time(train_ts)) %>% fitted() -> yhat.lm1
p1 <- autoplot(train_ts) + 
  geom_line(mapping=aes(x=time(train_ts), y=yhat.lm1), color="red")+
  ggtitle("Linear Trend Fit")+
  xlab("Time")+
  ylab("Pickups")

# code for differencing the training data
train_diff <- diff(train_ts)
lm(train_diff ~ time(train_diff)) %>% fitted() -> yhat.lm2

p2 <- train_diff %>% autoplot() + 
  geom_line(mapping=aes(x=time(train_diff), y=yhat.lm2), color="red") + 
  ggtitle("Differenced Pickup Data") + 
  ylab("Change in Pickups")

grid.arrange(p1, p2, nrow=1, ncol=2)

Zoom in

diffwin <- train_diff %>% window(end=decimal_date(ymd_hms("2015-03-01 00:00:00"))) 
diffwin %>% autoplot() + ggtitle("2 Months of Differenced Pickups")


diffwin %>% autoplot(series="Original Data")+
  autolayer(ma(diffwin, 4), series="Moving Average")+
  jan + feb + mar +
  guides(colour=guide_legend("Split"))+
  scale_color_manual(values=c("black", "grey"))+
  ggtitle("Moving Average of Training Data (Jan and Feb)")
p1 <- train_diff  %>% ggAcf() + ggtitle("ACF of Differenced Uber Pickups")
p2 <- train_diff %>% ggPacf() + ggtitle("PACF of Differenced Uber Pickups")
grid.arrange(p1, p2, nrow=1, ncol=2)
train_diff %>% Acf(plot=FALSE, lag.max=30)

Autocorrelations of series ‘.’, by lag

     0      1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20 
 1.000 -0.387 -0.168 -0.189  0.598 -0.307 -0.039 -0.118  0.274 -0.157  0.015 -0.129  0.233 -0.135  0.031 -0.125  0.220 -0.130  0.021 -0.149  0.260 
    21     22     23     24     25     26     27     28     29     30 
-0.117 -0.025 -0.291  0.558 -0.179 -0.145 -0.343  0.855 -0.337 -0.145 
# PACF of differenced data
train_diff %>% Pacf(plot=FALSE, lag.max=30)

Partial autocorrelations of series ‘.’, by lag

     1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20     21 
-0.387 -0.373 -0.577  0.246 -0.030  0.088  0.010 -0.163 -0.065 -0.090 -0.164  0.078 -0.058  0.005 -0.067 -0.042 -0.023 -0.068 -0.192  0.003 -0.031 
    22     23     24     25     26     27     28     29     30 
-0.066 -0.491  0.181  0.062 -0.101 -0.448  0.428  0.114  0.145 

There is clear seasonality and a sharp cutoff after lag 26-ish. Each lag is 1 shift, so 24 shifts would be 6 days of pickups, 26 shifts would be 6.5 days of pickups (evening shift of the 6th day).

In the ACF plot there is a spike at lag 4, and another lower spike at lag 8, lag 12, etc. In addition, there are large spikes at lag 28n. This indicates an AR(4) component and seasonal component at lag 28. The PACF spikes and cuts off after lag 3, and spikes again at lag 28. This further suggests an AR(4) or AR(3) model.

We looked at the lags for the differenced time series. We suspect that there is a positive autocorrelation at lag 4n because of assumed daily seasonality. The other place to check would be where the ACF’s seasonal decay exhibited spikes at lag 28n. Looking at the plot above it’s clear that this is a highly predictive lag.

Next we difference the series S=28 times for the D=1 seasonal difference. Here we can see what the order of the seasonal component would be.

train_sdiff <- train_ts %>% diff() %>% diff(lag=28)

p1 <- train_sdiff %>% ggAcf() + ggtitle("ACF of Diff(28) data")
p2 <- train_sdiff %>% ggPacf() + ggtitle("PACF of Diff(28) data")
grid.arrange(p1, p2, nrow=2, ncol=1)

# ACF of seasonal component
train_ts %>% diff() %>% diff(lag=28) %>% Acf(plot=FALSE, lag.max=30) 

Autocorrelations of series ‘.’, by lag

     0      1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20 
 1.000 -0.275 -0.206 -0.063  0.213 -0.073 -0.069  0.035 -0.008  0.006  0.002  0.010  0.014 -0.064  0.041 -0.018  0.013 -0.022  0.010 -0.026  0.042 
    21     22     23     24     25     26     27     28     29     30 
-0.038  0.066  0.021 -0.082 -0.017  0.138  0.087 -0.406  0.078  0.148 
# PACF of seasonal component
train_ts %>% diff() %>% diff(lag=28) %>% Pacf(plot=FALSE, lag.max=30) 

Partial autocorrelations of series ‘.’, by lag

     1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20     21 
-0.275 -0.305 -0.264  0.038 -0.059 -0.058  0.005 -0.070 -0.015  0.000 -0.004  0.040 -0.056  0.005 -0.035 -0.019 -0.012 -0.021 -0.046  0.016 -0.049 
    22     23     24     25     26     27     28     29     30 
 0.064  0.082 -0.039 -0.015  0.103  0.189 -0.298 -0.129 -0.075 

The ACF and PACF of the seasonal differenced data look very similar. There is a spike at lag 1 and at lag 28 (ACF) and lag 29 (PACF). Besides these two spikes, the rest of the values tend to stick around zero. This is indicative of an SMA(1) seasonal component. There doesn’t appear to be any SAR(P) component. Since we took one seasonal difference, then D=1.

We will now make an ARIMA model based on the ACF and PACF plots of the diff(1) and diff(28) version of our original data.

SARIMA Models

The first model is based on the model presented in the time series analysis book mixed with the seasonality that we observe in the Uber dataset. This is a model that is used in economics and was used to predict airline passengers in the book.

# Intuitive model based on the plots
m1 <- Arima(train_ts, 
      order=c(1, 1, 0), 
      seasonal=list(order=c(0,1,1), period=28))

yhat <- m1 %>% forecast(h=test_size)

# Plot the predictions
p1 <- yhat %>% autoplot() 
p2 <- ggAcf(residuals(m1)) + ggtitle("ACF of m1 Residuals")

grid.arrange(p1, p2, nrow=1, ncol=2)


# Print out summary of the coefficients
m1 %>% summary()
Series: train_ts 
ARIMA(1,1,0)(0,1,1)[28] 

Coefficients:
          ar1     sma1
      -0.3765  -0.9039
s.e.   0.0426   0.0498

sigma^2 estimated as 26438:  log likelihood=-3766.04
AIC=7538.09   AICc=7538.13   BIC=7551.15

Training set error measures:
                   ME   RMSE      MAE MPE MAPE MASE        ACF1
Training set 7.162789 158.37 98.86515 Inf  Inf  NaN -0.06150298

The pattern looks like it was captured in the predictive mean of the forecast, but the prediction intervals get really wide for forecasts beyond about 2 weeks. At this point, the prediction interval captures negative values.

Next we use p=4 since this is what was uncovered in the ACF and PACF plots of the differenced time series.

m2 <- Arima(train_ts, 
      order=c(4, 1, 0), 
      seasonal=list(order=c(0,1,1), period=28))

yhat2 <- m2 %>% forecast(h=test_size)

p1 <- yhat2 %>% autoplot()
p2 <- ggAcf(residuals(m2)) + ggtitle("ACF of Residuals")

grid.arrange(p1, p2, nrow=1, ncol=2)

This second model dips below zero in the prediction interval after the first week. The AIC and BIC of this model are also lower than that of the first economic model.

The last model is one that is chosen by auto.arima on the basis of AIC. However, the function needs a time series with frequency defined and I struggled to understand the frequency parameter. I made a guess that it would be the number of days in a year (1 season) divided by the value of the season present in my data.

# Auto arima with forced seasonality
train_seasonal <- ts(uber_train$East_Village, frequency=365.4/28)
m_auto <- auto.arima(train_seasonal, D=1) 
m_auto %>% forecast(h=test_size) %>% autoplot()

m_auto_y <- m_auto %>% forecast(h=test_size)
m_auto <- Arima(train_ts, 
      order=c(3, 0, 2), 
      seasonal=list(order=c(2,1,0), period=13))

m_auto_y <- m_auto %>% forecast(h=test_size)

This model does worse than the first two.

# Comparison of model results
p1 <- ggplot()+
  geom_point(mapping=aes(x=y_test, y=yhat$mean))+
  xlim(min(y_test), max(y_test))+
  ylim(min(y_test), max(y_test))+
  ggtitle("Actual vs. Predicted (m1)")

p2 <- ggplot()+
  geom_point(mapping=aes(x=y_test, y=yhat2$mean))+
  xlim(min(y_test), max(y_test))+
  ylim(min(y_test), max(y_test))+
  ggtitle("Actual vs. Predicted (m2)")

grid.arrange(p1, p2, nrow=1, ncol=2)

m7 <- Arima(train_ts, 
      order=c(4, 1, 0), 
      seasonal=list(order=c(0,1,4), period=28))

yhat7 <- m7 %>% forecast(h=test_size)

p1 <- yhat7 %>% autoplot()
p2 <- ggAcf(residuals(m7)) + ggtitle("ACF of Residuals")

grid.arrange(p1, p2, nrow=1, ncol=2)

BIC(m7)
[1] 7460.809
m8 <- Arima(train_ts, 
      order=c(3, 1, 0), 
      seasonal=list(order=c(0,1,1), period=28))

yhat8 <- m8 %>% forecast(h=test_size)

p1 <- yhat8 %>% autoplot()
p2 <- ggAcf(residuals(m8)) + ggtitle("ACF of Residuals")

grid.arrange(p1, p2, nrow=1, ncol=2)

Ljung-Box Tests

# Ljung Box tests
fit_test <- sarima(train_ts, p=4, d=1, q=0, P=0, D=1,  Q=1, S=28)

fit_test

m1 was pretty good but didn’t pass the Ljung-Box test of significance. However m2 did. And this also assumes normally-distributed errors which isn’t exactly true with our data since it’s Poisson distributed.

Poisson Models

# One-hot encoding function for weekly seasonal variable
dmy <- dummyVars(~season, data = uber_train)
trainCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_train$season)))
testCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_test$season)))

# 
m3 <- tsglm(train_ts, model=list(past_obs=1, past_mean=1), distr="poisson", xreg=trainCovariates)
At least one estimated parameter corresponding to a covariate is very close to
zero, the boundary of the parameter constraint for this parameter. This might
be an appropriate estimation but could also indicate that the parameter is
actually negative and the chosen model is too restrictive. In such a case one
could consider using a model with the logarithmic link function. Please check
results carefully.
yhat_m3 <- predict(m3, n.ahead=test_size, newxreg=testCovariates)

predint_m3 <- data.frame(yhat_m3$interval)

ggplot()+
  geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
  geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m3$lower, ymax = predint_m3$upper), fill="blue", alpha=0.5)+
  geom_line(mapping=aes(x=time(test_ts), y=yhat_m3$median), color="blue")+
  ggtitle("Poisson GLM with ARMA(1,1) and Weekday Dummy Variable")

m4 <- tsglm(train_ts, model=list(past_obs=1, past_mean=1), distr="poisson")

yhat_m4 <- predict(m4, n.ahead=test_size)

predint_m4 <- data.frame(yhat_m4$interval)

ggplot()+
  geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
  geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m4$lower, ymax = predint_m4$upper), fill="blue", alpha=0.5)+
  geom_line(mapping=aes(x=time(test_ts), y=yhat_m4$median), color="blue")+
  ggtitle("Poisson GLM with ARMA(1,1)")

mean((y_test - yhat_m6$median)^2)
[1] 29587.04
dmy <- dummyVars(~season, data = uber_train)
trainCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_train$season)))
testCovariates <- data.frame(predict(dmy ,newdata=data.frame(season=uber_test$season)))

m6 <- tsglm(train_ts, model=list(past_obs=4, past_mean=1), distr="poisson", xreg=trainCovariates)
predint_m6 <- data.frame(yhat_m6$interval)

yhat_m6 <- predict(m6, n.ahead=test_size, newxreg=testCovariates)

ggplot()+
  geom_line(mapping=aes(x=time(train_ts), y=train_ts))+
  geom_ribbon(mapping=aes(x=time(test_ts), ymin=predint_m6$lower, ymax = predint_m6$upper), fill="blue", alpha=0.5)+
  geom_line(mapping=aes(x=time(test_ts), y=yhat_m6$median), color="blue")+
  ggtitle("Poisson GLM ARMA(4,1) and Weekday Dummy Variable")

That works, should I include the AIC / BIC tables up here too? And the residuals plots and stuff? Alrighty, if you could just put an example section with your model and I can just copy the same format. I have plenty of BS I can stick in d:

Jk im gonna delete this so we have space haha but I’ll copy it in a separate texct file Could you include a quick writeup on it? I was gonna write up something quick about SARIMA, and William is gonna write up something about Poisson. Sounds good, would you be down to put it up here? We were trying to think of what to put up in this section (unless you have an idea of what to put . Sounds good

Our analysis considered three models:\ thats right, and multivariate generalized additive models, or VGAM’s.Sure, ill do a write up in the model fitting section, ill type out the actual models mathematically and try to explain them. yea i can write it here instead, for model fitting and validation put plots of your forecast beside the validation and talk about how well they fit the data. also probably include tables with the models and errors on the validation set. Yes, and the ljung box stuff too, the procedures you used to choose the best model should be there like the aic, but lets not go too crazy lol. I think at most we include validation error, aic, residual or Q-Q plots, and ljung box should be good. for sure, alright have fun lol

train_freq <- ts(uber_train$East_Village, frequency=28)
test_freq <- ts(uber_test$East_Village, frequency=28)

train_freq %>% auto.arima() -> m10

p1 <- m10 %>% forecast(h=120) %>% autoplot()
p2 <- m10 %>% residuals() %>% ggAcf() + ggtitle("ACF of Residuals")

grid.arrange(p1, p2, nrow=1, ncol=2)
checkresiduals(m10)

    Ljung-Box test

data:  Residuals from ARIMA(4,0,0)(2,1,0)[28]
Q* = 24.923, df = 50, p-value = 0.9989

Model df: 6.   Total lags used: 56

naives_m <- Arima(train_ts, 
      order=c(0, 0, 0), 
      seasonal=list(order=c(0,1,0), period=28))

sfit <- train_ts %>% snaive(m=28) 
p1 <- naives_m %>% forecast(h=test_size) %>% autoplot(PI=FALSE)
p2 <- sfit %>% forecast(h=test_size) %>% autoplot()

grid.arrange(p1, p2, nrow=2, ncol=1)

sfit <- snaive(train_ts) 
yhat_sfit <- sfit %>% forecast(h=test_size)
sfit %>% forecast(h=test_size) %>% autoplot()

Accuracy Measures

# SARIMA models
m1 <- Arima(train_ts, 
      order=c(1, 1, 0), 
      seasonal=list(order=c(0,1,1), period=28))

m2 <- Arima(train_ts, 
      order=c(4, 1, 0), 
      seasonal=list(order=c(0,1,1), period=28))

m3 <- Arima(train_ts, 
      order=c(4, 1, 0), 
      seasonal=list(order=c(0,1,4), period=28))

m4 <- Arima(train_ts, 
      order=c(3, 0, 2), 
      seasonal=list(order=c(2,1,0), period=13))

m5 <- Arima(train_ts, 
      order=c(4, 0, 0), 
      seasonal=list(order=c(2,1,0), period=28))
accuracy(yhat_m1, test_ts)
                      ME     RMSE       MAE      MPE     MAPE MASE        ACF1 Theil's U
Training set    7.162789 158.3700  98.86515      Inf      Inf  NaN -0.06150298        NA
Test set     -183.437561 494.5975 411.62220 -67.0349 88.51759  NaN -0.34752397 0.9912245
accuracy(yhat_m2, test_ts)
                      ME     RMSE       MAE       MPE     MAPE MASE       ACF1 Theil's U
Training set    7.269319 142.9743  92.78463      -Inf      Inf  NaN  0.0640973        NA
Test set     -110.032570 471.5929 388.80887 -51.40147 79.25787  NaN -0.3495840 0.9525737
accuracy(yhat_m3, test_ts)
                     ME     RMSE       MAE       MPE     MAPE MASE        ACF1 Theil's U
Training set   7.317047 138.2777  90.03849      -Inf      Inf  NaN  0.06230789        NA
Test set     -90.257804 461.6869 380.91922 -47.90756 76.96151  NaN -0.34702807  0.920203
accuracy(yhat_m4, test_ts)
                    ME     RMSE     MAE       MPE     MAPE MASE          ACF1 Theil's U
Training set  5.363351 331.8169 243.259      -Inf      Inf  NaN -0.0003350836        NA
Test set     -1.991453 409.3317 326.022 -31.38139 65.15798  NaN -0.0211198402 0.7923496
accuracy(yhat_m5, test_ts)
                    ME     RMSE       MAE       MPE     MAPE MASE         ACF1 Theil's U
Training set  6.122341 146.6422  89.46945      -Inf      Inf  NaN  0.007535442        NA
Test set     20.745266 446.7924 362.64753 -24.49447 65.80993  NaN -0.325192843 0.9155374
accuracy(yhat_naive, test_ts)
                    ME     RMSE      MAE      MPE    MAPE MASE       ACF1 Theil's U
Training set  9.250045 197.3485 112.4388     -Inf     Inf  NaN  0.3886022        NA
Test set     47.924370 445.6627 359.8908 -16.1501 63.4041  NaN -0.3310975 0.9683322
mae_tsglm28
[1] 90.35833
LS0tCnRpdGxlOiAiVWJlciBUcmlwcyBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKClRvIHVwbG9hZCB0aGUgZGF0YSwgZG93bmxvYWQgdGhlIGB1YmVyX3BpY2t1cHNfbG93ZXJfbWFuaGF0dGFuX3dpZGVfNmguY3N2YCBhbmQgc3RpY2sgaXQgaW4gYSBmb2xkZXIgYW5kIHJlbWVtYmVyIHRoZSBkaXJlY3RvcnkuIAoKYGBge3J9CiMgSW5zdGFsbCByZXF1aXJlZCBsaWJyYXJpZXMgaWYgbmVjZXNzYXJ5LgpyZXF1aXJlKCJmb3JlY2FzdCIpCnJlcXVpcmUoImx1YnJpZGF0ZSIpCnJlcXVpcmUoImdyaWRFeHRyYSIpCnJlcXVpcmUoInRpZHl2ZXJzZSIpCnJlcXVpcmUoImNhcmV0IikKcmVxdWlyZSgidHNjb3VudCIpCmBgYAoKYGBge3J9CiMgSW5wdXQgZGF0YQp1YmVyXzZoIDwtIHJlYWRfY3N2KCcuLi9kYXRhL3ViZXJfcGlja3Vwc19sb3dlcl9tYW5oYXR0YW5fd2lkZV82aC5jc3YnKSAjIFBvaW50IHRoaXMgdG8gdGhlIGRpcmVjdG9yeSBhbmQgZmlsZQoKdWJlcl90cmFpbiA8LSB1YmVyXzZoICU+JSBmaWx0ZXIoUGlja3VwX2RhdGUgPCB5bWRfaG1zKCIyMDE1LTA2LTAxIDAwOjAwOjAwIikpICMgVGhpcyBnaXZlcyB1cyBhIHRyYWluaW5nIHNldCBmb3IgYWxsIDggbG9jYXRpb25zCnViZXJfdGVzdCA8LSB1YmVyXzZoICU+JSBmaWx0ZXIoUGlja3VwX2RhdGUgPj0geW1kX2htcygiMjAxNS0wNi0wMSAwMDowMDowMCIpKQp1YmVyX3RyYWluIDwtIHViZXJfdHJhaW4gJT4lIG11dGF0ZShzZWFzb249d2Vla2RheXMoUGlja3VwX2RhdGUsIGFiYnJldmlhdGU9VFJVRSkpCnViZXJfdGVzdCA8LSB1YmVyX3Rlc3QgJT4lIG11dGF0ZShzZWFzb249d2Vla2RheXMoUGlja3VwX2RhdGUsIGFiYnJldmlhdGU9VFJVRSkpCiMgbnJvdyh1YmVyXzZoKSAjIGlmIHdlIHdhbnQgdG8gY2hlY2sgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwojIG5hbWVzKHViZXJfNmgpICMgdXNlIHRoaXMgY29tbWFuZCBpZiB5b3Ugd2FudCB0byBzZWUgdGhlIGF2YWlsYWJsZSB0aW1lIHNlcmllcwp0cmFpbl9mcmVxIDwtIHRzKHViZXJfdHJhaW4kRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MjgpCnRlc3RfZnJlcSA8LSB0cyh1YmVyX3Rlc3QkRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MjgpCmBgYAoKYGBge3J9CmhlYWQodWJlcl90ZXN0KQpgYGAKCgojIyBUcmFpbi10ZXN0IHNwbGl0CgpXZSB3aWxsIHVzZSB0aGUgZmlyc3QgZml2ZSBtb250aHMgYXMgdHJhaW5pbmcgZGF0YSBhbmQgcHJlZGljdCBVYmVyIHBpY2t1cHMgZm9yIEp1bmUgMjAxNSBpbiB0aGUgRWFzdCBWaWxsYWdlLiBUaGUgZGF0ZSBpcyBpbiBwb3NpdHggZm9ybWF0IHdoaWNoIGNhbiBiZSBwYXJzZWQgdXNpbmcgdGhlIGx1YnJpZGF0ZSBwYWNrYWdlLiAKClN0YXRpc3RpY2FsIGZvcmVjYXN0aW5nIHRlY2huaXF1ZXMgYXJlIHVzZWQgb24gdGhlIEVhc3QgVmlsbGFnZSB0cmFpbmluZyBkYXRhLCBhbmQgd2lsbCBiZSBldmFsdWF0ZWQgb24gRWFzdCBWaWxsYWdlIHRlc3QgZGF0YS4gCgoKYGBge3J9CiMgVGhpcyBibG9jayBzZWxlY3RzIG9ubHkgZWFzdCB2aWxsYWdlIGRhdGEgYW5kIHNwbGl0cyBpdCBpbnRvIHRyYWluL3Rlc3Qgc2V0cwpmdWxsX3RzIDwtIG1zdHModWJlcl82aCRFYXN0X1ZpbGxhZ2UsCiAgICAgICAgICAgICAgICAgICAgc3RhcnQ9ZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDEtMDEgMDA6MDA6MDAiKSksCiAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwucGVyaW9kcz1jKDQsIDE0NjEpKQoKdHJhaW5fdHMgPC0gbXN0cyh1YmVyX3RyYWluJEVhc3RfVmlsbGFnZSwKICAgICAgICAgICAgICAgICAgICBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMS0wMSAwMDowMDowMCIpKSwKICAgICAgICAgICAgICAgICAgICBzZWFzb25hbC5wZXJpb2RzPWMoNCwgMTQ2MSkpCgp0ZXN0X3RzIDwtIG1zdHModWJlcl90ZXN0JEVhc3RfVmlsbGFnZSwKICAgICAgICAgICAgICAgICAgICBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wNi0wMSAwMDowMDowMCIpKSwKICAgICAgICAgICAgICAgICAgICBzZWFzb25hbC5wZXJpb2RzPWMoNCwgMTQ2MSkpCgoKI3RyYWluX3RzIDwtIHdpbmRvdyhmdWxsX3RzLCBzdGFydD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMS0wMSAwMDowMDowMCIpKSkgIyBUcmFpbiB3aXRoIG9ic2VyY2F0aW9ucyB1cCB1bnRpbCBqdW5lCgojdGVzdF90cyA8LSB3aW5kb3coZnVsbF90cywgc3RhcnQ9ZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDYtMDEgMDA6MDA6MDAiKSkpICMgVGVzdCBvdXQgcHJlZGljdGlvbnMgd2l0aCBKdW5lIGRhdGEKeV90ZXN0IDwtIGFzLm51bWVyaWModGVzdF90cykKdGVzdF9zaXplIDwtIGxlbmd0aCh5X3Rlc3QpCmBgYAoKCmBgYHtyfQojIFNob3cgZnVsbCBkYXRhIHNldApwMSA8LSBmdWxsX3RzICU+JSBhdXRvcGxvdChzZXJpZXM9IkFsbCBUcmlwcyIpICsgCiAgZ2d0aXRsZSgiRnVsbCBEYXRhc2V0IikgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJEYXRhIikpKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siKSkKCiMgU2hvdyB0cmFpbmluZyBkYXRhc2V0CnAyIDwtIGF1dG9wbG90KHRyYWluX3RzLCBzZXJpZXM9IlRyYWluaW5nIikrCiAgYXV0b2xheWVyKHRlc3RfdHMsICBzZXJpZXM9IlZhbGlkYXRpb24iKSsKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCgiU3BsaXQiKSkrCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsICJncmV5IikpKwogIGdndGl0bGUoIlRyYWluL1Rlc3QgU3BsaXQiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKVG8gbG9vayBhdCBhbnkgb2YgdGhlIG90aGVyIDcgcGlja3VwIGxvY2F0aW9ucywgY2FsbCBgbGlsX2l0YWx5IDwtIHViZXJfdHJhaW4gJT4lIHNlbGVjdChMaXR0bGVfSXRhbHkpYCBmb3IgZXhhbXBsZS4KCmBgYHtyfQojIFNob3cgc3Vic2V0IG9mIGRhdGEgdG8gbG9vayBmb3IgcGF0dGVybnMKCmphbl90b19tYXJjaCA8LSB0cmFpbl90cyAlPiUgd2luZG93KGVuZD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMy0wMSAwMDowMDowMCIpKSkKCmphbl90b19tYXJjaCAlPiUgYXV0b3Bsb3QoKSsKICBnZ3RpdGxlKCIyIE1vbnRocyBvZiBVYmVyIFBpY2t1cCBEYXRhIikKYGBgCgoKQnkgYWdncmVnYXRpbmcgdG8gNi1ob3VyIHdpbmRvd3Mgd2UgbW9kZWwgdGhlIGRlbWFuZCBwZXIgInNoaWZ0IiBpbiBhIGRheS4KCi0gMDA6MDAgLSAwNTo1OSAtIEdyYXZleWFyZCBTaGlmdAotIDA2OjAwIC0gMTE6NTkgLSBNb3JuaW5nIFNoaWZ0Ci0gMTI6MDAgLSAxNzo1OSAtIEFmdGVybm9vbiBTaGlmdAotIDE4OjAwIC0gMjM6NTkgLSBFdmVuaW5nIFNoaWZ0CgojIyBNb3ZpbmcgQXZlcmFnZSBGaWx0ZXIKCmBgYHtyfQpqYW4gPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDEtMDEgMDA6MDA6MDAiKSksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibHVlIiwgc2l6ZT0wLjUpCmZlYiA8LSBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMi0wMSAwMDowMDowMCIpKSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiLCBzaXplPTAuNSkKbWFyIDwtIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGRlY2ltYWxfZGF0ZSh5bWRfaG1zKCIyMDE1LTAzLTAxIDAwOjAwOjAwIikpLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIsIHNpemU9MC41KQphcHIgPC0gZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZGVjaW1hbF9kYXRlKHltZF9obXMoIjIwMTUtMDQtMDEgMDA6MDA6MDAiKSksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJibHVlIiwgc2l6ZT0wLjUpCm1heSA8LSBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBkZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wNS0wMSAwMDowMDowMCIpKSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiLCBzaXplPTAuNSkKanVuIDwtIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGRlY2ltYWxfZGF0ZSh5bWRfaG1zKCIyMDE1LTA2LTAxIDAwOjAwOjAwIikpLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIsIHNpemU9MC41KQoKdHJhaW5fdHMgJT4lIGF1dG9wbG90KHNlcmllcz0iT3JpZ2luYWwgRGF0YSIpKwogIGF1dG9sYXllcihtYSh0cmFpbl90cywgNCksIHNlcmllcz0iTW92aW5nIEF2ZXJhZ2UiKSsKICBqYW4gKyBmZWIgKyBtYXIgKyBhcHIgKyBtYXkgKyBqdW4gKyAjIGFkZGluZyBtb250aCBsaW5lcwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJTcGxpdCIpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImdyZXkiKSkrCiAgZ2d0aXRsZSgiTW92aW5nIEF2ZXJhZ2Ugb2YgVHJhaW5pbmcgRGF0YSIpCgpgYGAKClRoaXMgaGVscHMgd2l0aCB0ZWxsaW5nIHRoZSBzZWFzb25hbGl0eSBvZiB0aGUgZGF0YS4gSXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIHNwaWtlIGluIGRlbWFuZCBhdCB0aGUgYmVnaW5uaW5nIG9mIGVhY2ggbW9udGguIEluIGFkZGl0aW9uIHRoZXJlIGFyZSBhYm91dCB0aHJlZSBzcGlrZXMgZHVyaW5nIGVhY2ggbW9udGguIFRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gX193ZWVrbHkgc3Bpa2VzIGluIGRlbWFuZF9fLiAKCiMjIEFuYWx5emluZyB0aGUgdHJlbmQKCmBgYHtyfQpsbSh0cmFpbl90cyB+IHRpbWUodHJhaW5fdHMpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbQphdXRvcGxvdCh0cmFpbl90cykgKyAKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT15aGF0LmxtKSwgY29sb3I9InJlZCIpKwogIGdndGl0bGUoIkxpbmVhciBUcmVuZCBGaXQiKSsKICB4bGFiKCJUaW1lIikrCiAgeWxhYigiUGlja3VwcyIpCmBgYAoKClRoZXJlIGlzIGEgc2xpZ2h0IGxpbmVhciB0cmVuZC4gTGV0J3Mgc2VlIHdoYXQgdGhlIEFDRiBhbmQgUEFDRiBwbG90cyBsb29rIGxpa2UuCgoKYGBge3J9CnAxIDwtIGdnQWNmKHRyYWluX3RzKSArIGdndGl0bGUoIkFDRiBvZiBVYmVyIFBpY2t1cHMiKQpwMiA8LSBnZ1BhY2YodHJhaW5fdHMpICsgZ2d0aXRsZSgiUEFDRiBvZiBVYmVyIFBpY2t1cHMiKQpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTIsIG5jb2w9MSkKYGBgCgoKCk5vdCB2ZXJ5IGluZm9ybWF0aXZlIGFsdGhvdWdoIGl0IGluaXRpYWxseSBoaW50cyB0b3dhcmQgYW4gQVIocCkgbW9kZWwgYWZ0ZXIgYSBkaWZmZXJlbmNlLiAKCkJlZm9yZSBjaGVja2luZyB0aGUgbGFnIHBsb3QsIHRoZSB0cmVuZCBzaG91bGQgYmUgcmVtb3ZlZCB2aWEgZGlmZmVyZW5jaW5nLgoKIyMgRGlmZmVyZW5jaW5nIAoKYGBge3J9CiMgY29kZSBmb3IgZGlmZmVyZW5jaW5nIHRoZSB0cmFpbmluZyBkYXRhCnRyYWluX2RpZmYgPC0gZGlmZih0cmFpbl90cykKbG0odHJhaW5fZGlmZiB+IHRpbWUodHJhaW5fZGlmZikpICU+JSBmaXR0ZWQoKSAtPiB5aGF0LmxtCgp0cmFpbl9kaWZmICU+JSBhdXRvcGxvdCgpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0cmFpbl9kaWZmKSwgeT15aGF0LmxtKSwgY29sb3I9InJlZCIpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW5jZWQgUGlja3VwIERhdGEiKSArIAogIHlsYWIoIkNoYW5nZSBpbiBQaWNrdXBzIikKYGBgCgpgYGB7cn0KIyBDb21wYXJpc29uIG9mIHRpbWUgc2VyaWVzIHBsb3RzCgpsbSh0cmFpbl90cyB+IHRpbWUodHJhaW5fdHMpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbTEKcDEgPC0gYXV0b3Bsb3QodHJhaW5fdHMpICsgCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0cmFpbl90cyksIHk9eWhhdC5sbTEpLCBjb2xvcj0icmVkIikrCiAgZ2d0aXRsZSgiT3JpZ2luYWwgRGF0YSB3aXRoIExpbmVhciBGaXQiKSsKICB4bGFiKCJUaW1lIikrCiAgeWxhYigiUGlja3VwcyIpCgojIGNvZGUgZm9yIGRpZmZlcmVuY2luZyB0aGUgdHJhaW5pbmcgZGF0YQp0cmFpbl9kaWZmIDwtIGRpZmYodHJhaW5fdHMpCmxtKHRyYWluX2RpZmYgfiB0aW1lKHRyYWluX2RpZmYpKSAlPiUgZml0dGVkKCkgLT4geWhhdC5sbTIKCnAyIDwtIHRyYWluX2RpZmYgJT4lIGF1dG9wbG90KCkgKyAKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX2RpZmYpLCB5PXloYXQubG0yKSwgY29sb3I9InJlZCIpICsgCiAgZ2d0aXRsZSgiRGlmZmVyZW5jZWQgRGF0YSB3aXRoIExpbmVhciBGaXQiKSArIAogIHlsYWIoIkNoYW5nZSBpbiBQaWNrdXBzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKCgoKIyMgWm9vbSBpbgoKYGBge3J9CmRpZmZ3aW4gPC0gdHJhaW5fZGlmZiAlPiUgd2luZG93KGVuZD1kZWNpbWFsX2RhdGUoeW1kX2htcygiMjAxNS0wMy0wMSAwMDowMDowMCIpKSkgCmRpZmZ3aW4gJT4lIGF1dG9wbG90KCkgKyBnZ3RpdGxlKCIyIE1vbnRocyBvZiBEaWZmZXJlbmNlZCBQaWNrdXBzIikKYGBgCgpgYGB7cn0KCmRpZmZ3aW4gJT4lIGF1dG9wbG90KHNlcmllcz0iT3JpZ2luYWwgRGF0YSIpKwogIGF1dG9sYXllcihtYShkaWZmd2luLCAyOCksIHNlcmllcz0iTW92aW5nIEF2ZXJhZ2UiKSsKICBqYW4gKyBmZWIgKyBtYXIgKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKCJTcGxpdCIpKSsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwgImdyZXkiKSkrCiAgZ2d0aXRsZSgiTW92aW5nIEF2ZXJhZ2Ugb2YgVHJhaW5pbmcgRGF0YSAoSmFuIGFuZCBGZWIpIikKYGBgCgpgYGB7cn0KcDEgPC0gdHJhaW5fZGlmZiAgJT4lIGdnQWNmKCkgKyBnZ3RpdGxlKCJBQ0Ygb2YgRGlmZmVyZW5jZWQgVWJlciBQaWNrdXBzIikKcDIgPC0gdHJhaW5fZGlmZiAlPiUgZ2dQYWNmKCkgKyBnZ3RpdGxlKCJQQUNGIG9mIERpZmZlcmVuY2VkIFViZXIgUGlja3VwcyIpCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKCgpgYGB7cn0KIyBBQ0Ygb2YgZGlmZmVyZW5jZWQgZGF0YQp0cmFpbl9kaWZmICU+JSBBY2YocGxvdD1GQUxTRSwgbGFnLm1heD0zMCkKYGBgCgpgYGB7cn0KIyBQQUNGIG9mIGRpZmZlcmVuY2VkIGRhdGEKdHJhaW5fZGlmZiAlPiUgUGFjZihwbG90PUZBTFNFLCBsYWcubWF4PTMwKQpgYGAKCgpUaGVyZSBpcyBjbGVhciBzZWFzb25hbGl0eSBhbmQgYSBzaGFycCBjdXRvZmYgYWZ0ZXIgbGFnIDI2LWlzaC4gRWFjaCBsYWcgaXMgMSBzaGlmdCwgc28gMjQgc2hpZnRzIHdvdWxkIGJlIDYgZGF5cyBvZiBwaWNrdXBzLCAyNiBzaGlmdHMgd291bGQgYmUgNi41IGRheXMgb2YgcGlja3VwcyAoZXZlbmluZyBzaGlmdCBvZiB0aGUgNnRoIGRheSkuIAoKSW4gdGhlIEFDRiBwbG90IHRoZXJlIGlzIGEgc3Bpa2UgYXQgbGFnIDQsIGFuZCBhbm90aGVyIGxvd2VyIHNwaWtlIGF0IGxhZyA4LCBsYWcgMTIsIGV0Yy4gSW4gYWRkaXRpb24sIHRoZXJlIGFyZSBsYXJnZSBzcGlrZXMgYXQgbGFnIDI4bi4gVGhpcyBpbmRpY2F0ZXMgYW4gQVIoNCkgY29tcG9uZW50IGFuZCBzZWFzb25hbCBjb21wb25lbnQgYXQgbGFnIDI4LiBUaGUgUEFDRiBzcGlrZXMgYW5kIGN1dHMgb2ZmIGFmdGVyIGxhZyAzLCBhbmQgc3Bpa2VzIGFnYWluIGF0IGxhZyAyOC4gVGhpcyBmdXJ0aGVyIHN1Z2dlc3RzIGFuIEFSKDQpIG9yIEFSKDMpIG1vZGVsLgoKYGBge3J9CmdnbGFncGxvdCh0cmFpbl9kaWZmLCBzZXQubGFncz0gYyg0LCA4LCAxMiwgMjgsIDU2LCA4NCksIGRvLmxpbmVzPUZBTFNFLCBjb2xvdXI9RkFMU0UpICsgZ2d0aXRsZSgiQXV0b2NvcnJlbGF0aW9uIGZvciBkaWZmZXJlbnQgbGFnczogVHJhaW4gRGlmZiIpCmBgYAoKV2UgbG9va2VkIGF0IHRoZSBsYWdzIGZvciB0aGUgZGlmZmVyZW5jZWQgdGltZSBzZXJpZXMuIFdlIHN1c3BlY3QgdGhhdCB0aGVyZSBpcyBhIHBvc2l0aXZlIGF1dG9jb3JyZWxhdGlvbiBhdCBsYWcgNG4gYmVjYXVzZSBvZiBhc3N1bWVkIGRhaWx5IHNlYXNvbmFsaXR5LiBUaGUgb3RoZXIgcGxhY2UgdG8gY2hlY2sgd291bGQgYmUgd2hlcmUgdGhlIEFDRidzIHNlYXNvbmFsIGRlY2F5IGV4aGliaXRlZCBzcGlrZXMgYXQgbGFnIDI4bi4gTG9va2luZyBhdCB0aGUgcGxvdCBhYm92ZSBpdCdzIGNsZWFyIHRoYXQgdGhpcyBpcyBhIGhpZ2hseSBwcmVkaWN0aXZlIGxhZy4KCk5leHQgd2UgZGlmZmVyZW5jZSB0aGUgc2VyaWVzIFM9MjggdGltZXMgZm9yIHRoZSBEPTEgc2Vhc29uYWwgZGlmZmVyZW5jZS4gSGVyZSB3ZSBjYW4gc2VlIHdoYXQgdGhlIG9yZGVyIG9mIHRoZSBzZWFzb25hbCBjb21wb25lbnQgd291bGQgYmUuCgpgYGB7cn0KdHJhaW5fc2RpZmYgPC0gdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpCgpwMSA8LSB0cmFpbl9zZGlmZiAlPiUgZ2dBY2YoKSArIGdndGl0bGUoIkFDRiBvZiBEaWZmKDI4KSBkYXRhIikKcDIgPC0gdHJhaW5fc2RpZmYgJT4lIGdnUGFjZigpICsgZ2d0aXRsZSgiUEFDRiBvZiBEaWZmKDI4KSBkYXRhIikKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKCmBgYHtyfQojIEFDRiBvZiBzZWFzb25hbCBjb21wb25lbnQKdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpICU+JSBBY2YocGxvdD1GQUxTRSwgbGFnLm1heD0zMCkgCmBgYAoKYGBge3J9CiMgUEFDRiBvZiBzZWFzb25hbCBjb21wb25lbnQKdHJhaW5fdHMgJT4lIGRpZmYoKSAlPiUgZGlmZihsYWc9MjgpICU+JSBQYWNmKHBsb3Q9RkFMU0UsIGxhZy5tYXg9MzApIApgYGAKClRoZSBBQ0YgYW5kIFBBQ0Ygb2YgdGhlIHNlYXNvbmFsIGRpZmZlcmVuY2VkIGRhdGEgbG9vayB2ZXJ5IHNpbWlsYXIuIFRoZXJlIGlzIGEgc3Bpa2UgYXQgbGFnIDEgYW5kIGF0IGxhZyAyOCAoQUNGKSBhbmQgbGFnIDI5IChQQUNGKS4gQmVzaWRlcyB0aGVzZSB0d28gc3Bpa2VzLCB0aGUgcmVzdCBvZiB0aGUgdmFsdWVzIHRlbmQgdG8gc3RpY2sgYXJvdW5kIHplcm8uIFRoaXMgaXMgaW5kaWNhdGl2ZSBvZiBhbiBTTUEoMSkgc2Vhc29uYWwgY29tcG9uZW50LiBUaGVyZSBkb2Vzbid0IGFwcGVhciB0byBiZSBhbnkgU0FSKFApIGNvbXBvbmVudC4gU2luY2Ugd2UgdG9vayBvbmUgc2Vhc29uYWwgZGlmZmVyZW5jZSwgdGhlbiBEPTEuCgpXZSB3aWxsIG5vdyBfX21ha2UgYW4gQVJJTUEgbW9kZWwgYmFzZWQgb24gdGhlIEFDRiBhbmQgUEFDRiBwbG90cyBvZiB0aGUgZGlmZigxKSBhbmQgZGlmZigyOClfXyB2ZXJzaW9uIG9mIG91ciBvcmlnaW5hbCBkYXRhLiAKCgojIyBTQVJJTUEgTW9kZWxzCgoKVGhlIGZpcnN0IG1vZGVsIGlzIGJhc2VkIG9uIHRoZSBtb2RlbCBwcmVzZW50ZWQgaW4gdGhlIHRpbWUgc2VyaWVzIGFuYWx5c2lzIGJvb2sgbWl4ZWQgd2l0aCB0aGUgc2Vhc29uYWxpdHkgdGhhdCB3ZSBvYnNlcnZlIGluIHRoZSBVYmVyIGRhdGFzZXQuIFRoaXMgaXMgYSBtb2RlbCB0aGF0IGlzIHVzZWQgaW4gZWNvbm9taWNzIGFuZCB3YXMgdXNlZCB0byBwcmVkaWN0IGFpcmxpbmUgcGFzc2VuZ2VycyBpbiB0aGUgYm9vay4gCgpgYGB7cn0KIyBJbnR1aXRpdmUgbW9kZWwgYmFzZWQgb24gdGhlIHBsb3RzCm0xIDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9YygxLCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsMSksIHBlcmlvZD0yOCkpCgp5aGF0IDwtIG0xICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKCiMgUGxvdCB0aGUgcHJlZGljdGlvbnMKcDEgPC0geWhhdCAlPiUgYXV0b3Bsb3QoKSAKcDIgPC0gZ2dBY2YocmVzaWR1YWxzKG0xKSkgKyBnZ3RpdGxlKCJBQ0Ygb2YgbTEgUmVzaWR1YWxzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQoKIyBQcmludCBvdXQgc3VtbWFyeSBvZiB0aGUgY29lZmZpY2llbnRzCm0xICU+JSBzdW1tYXJ5KCkKCm0xX3kgPC0gbTEgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpgYGAKClRoZSBwYXR0ZXJuIGxvb2tzIGxpa2UgaXQgd2FzIGNhcHR1cmVkIGluIHRoZSBwcmVkaWN0aXZlIG1lYW4gb2YgdGhlIGZvcmVjYXN0LCBidXQgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGdldCByZWFsbHkgd2lkZSBmb3IgZm9yZWNhc3RzIGJleW9uZCBhYm91dCAyIHdlZWtzLiBBdCB0aGlzIHBvaW50LCB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbCBjYXB0dXJlcyBuZWdhdGl2ZSB2YWx1ZXMuIAoKTmV4dCB3ZSB1c2UgcD00IHNpbmNlIHRoaXMgaXMgd2hhdCB3YXMgdW5jb3ZlcmVkIGluIHRoZSBBQ0YgYW5kIFBBQ0YgcGxvdHMgb2YgdGhlIGRpZmZlcmVuY2VkIHRpbWUgc2VyaWVzLgoKCmBgYHtyfQptMiA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoNCwgMSwgMCksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMCwxLDEpLCBwZXJpb2Q9MjgpKQoKCm0yX3kgPC0gbTIgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQoKcDEgPC0geWhhdDIgJT4lIGF1dG9wbG90KCkKcDIgPC0gZ2dBY2YocmVzaWR1YWxzKG0yKSkgKyBnZ3RpdGxlKCJBQ0Ygb2YgUmVzaWR1YWxzIikKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3c9MSwgbmNvbD0yKQpgYGAKClRoaXMgc2Vjb25kIG1vZGVsIGRpcHMgYmVsb3cgemVybyBpbiB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbCBhZnRlciB0aGUgZmlyc3Qgd2Vlay4gVGhlIEFJQyBhbmQgQklDIG9mIHRoaXMgbW9kZWwgYXJlIGFsc28gbG93ZXIgdGhhbiB0aGF0IG9mIHRoZSBmaXJzdCBlY29ub21pYyBtb2RlbC4KClRoZSBsYXN0IG1vZGVsIGlzIG9uZSB0aGF0IGlzIGNob3NlbiBieSBgYXV0by5hcmltYWAgb24gdGhlIGJhc2lzIG9mIEFJQy4gSG93ZXZlciwgdGhlIGZ1bmN0aW9uIG5lZWRzIGEgdGltZSBzZXJpZXMgd2l0aCBmcmVxdWVuY3kgZGVmaW5lZCBhbmQgSSBzdHJ1Z2dsZWQgdG8gdW5kZXJzdGFuZCB0aGUgZnJlcXVlbmN5IHBhcmFtZXRlci4gSSBtYWRlIGEgZ3Vlc3MgdGhhdCBpdCB3b3VsZCBiZSB0aGUgbnVtYmVyIG9mIGRheXMgaW4gYSB5ZWFyICgxIHNlYXNvbikgZGl2aWRlZCBieSB0aGUgdmFsdWUgb2YgdGhlIHNlYXNvbiBwcmVzZW50IGluIG15IGRhdGEuCgpgYGB7cn0KIyBBdXRvIGFyaW1hIHdpdGggZm9yY2VkIHNlYXNvbmFsaXR5CnRyYWluX3NlYXNvbmFsIDwtIHRzKHViZXJfdHJhaW4kRWFzdF9WaWxsYWdlLCBmcmVxdWVuY3k9MzY1LjQvMjgpCm1fYXV0byA8LSBhdXRvLmFyaW1hKHRyYWluX3NlYXNvbmFsLCBEPTEpIAptX2F1dG8gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoKQptX2F1dG9feSA8LSBtX2F1dG8gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpgYGAKCmBgYHtyfQptX2F1dG8gPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDMsIDAsIDIpLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDIsMSwwKSwgcGVyaW9kPTEzKSkKCm1fYXV0b195IDwtIG1fYXV0byAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCmBgYAoKYGBge3J9CnNmaXQgPC0gc25haXZlKHRyYWluX3RzKSAKeWhhdF9zZml0IDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpzZml0ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkgJT4lIGF1dG9wbG90KCkKYGBgCgoKClRoaXMgbW9kZWwgZG9lcyB3b3JzZSB0aGFuIHRoZSBmaXJzdCB0d28uIAoKYGBge3J9CiMgQ29tcGFyaXNvbiBvZiBtb2RlbCByZXN1bHRzCnAxIDwtIGdncGxvdCgpKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD15X3Rlc3QsIHk9eWhhdCRtZWFuKSkrCiAgeGxpbShtaW4oeV90ZXN0KSwgbWF4KHlfdGVzdCkpKwogIHlsaW0obWluKHlfdGVzdCksIG1heCh5X3Rlc3QpKSsKICBnZ3RpdGxlKCJBY3R1YWwgdnMuIFByZWRpY3RlZCAobTEpIikKCnAyIDwtIGdncGxvdCgpKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD15X3Rlc3QsIHk9eWhhdDIkbWVhbikpKwogIHhsaW0obWluKHlfdGVzdCksIG1heCh5X3Rlc3QpKSsKICB5bGltKG1pbih5X3Rlc3QpLCBtYXgoeV90ZXN0KSkrCiAgZ2d0aXRsZSgiQWN0dWFsIHZzLiBQcmVkaWN0ZWQgKG0yKSIpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTEsIG5jb2w9MikKYGBgCgoKYGBge3J9Cm03IDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9Yyg0LCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsNCksIHBlcmlvZD0yOCkpCgptN195IDwtIG03ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKCnAxIDwtIHloYXQ3ICU+JSBhdXRvcGxvdCgpCnAyIDwtIGdnQWNmKHJlc2lkdWFscyhtNykpICsgZ2d0aXRsZSgiQUNGIG9mIFJlc2lkdWFscyIpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBucm93PTEsIG5jb2w9MikKYGBgCgpgYGB7cn0KQklDKG0yKQpCSUMobTcpCmBgYAoKYGBge3J9Cm04IDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9YygzLCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsMSksIHBlcmlvZD0yOCkpCgp5aGF0OCA8LSBtOCAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCgpwMSA8LSB5aGF0OCAlPiUgYXV0b3Bsb3QoKQpwMiA8LSBnZ0FjZihyZXNpZHVhbHMobTgpKSArIGdndGl0bGUoIkFDRiBvZiBSZXNpZHVhbHMiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0xLCBuY29sPTIpCmBgYAoKIyMjIExqdW5nLUJveCBUZXN0cwoKYGBge3J9CiMgTGp1bmcgQm94IHRlc3RzCmZpdF90ZXN0IDwtIHNhcmltYSh0cmFpbl90cywgcD00LCBkPTEsIHE9MCwgUD0wLCBEPTEsICBRPTEsIFM9MjgpCgpmaXRfdGVzdApgYGAKCmBtMWAgd2FzIHByZXR0eSBnb29kIGJ1dCBkaWRuJ3QgcGFzcyB0aGUgTGp1bmctQm94IHRlc3Qgb2Ygc2lnbmlmaWNhbmNlLiBIb3dldmVyIGBtMmAgZGlkLiBBbmQgdGhpcyBhbHNvIGFzc3VtZXMgbm9ybWFsbHktZGlzdHJpYnV0ZWQgZXJyb3JzIHdoaWNoIGlzbid0IGV4YWN0bHkgdHJ1ZSB3aXRoIG91ciBkYXRhIHNpbmNlIGl0J3MgUG9pc3NvbiBkaXN0cmlidXRlZC4gCgoKIyMgUG9pc3NvbiBNb2RlbHMKCmBgYHtyfQojIE9uZS1ob3QgZW5jb2RpbmcgZnVuY3Rpb24gZm9yIHdlZWtseSBzZWFzb25hbCB2YXJpYWJsZQpkbXkgPC0gZHVtbXlWYXJzKH5zZWFzb24sIGRhdGEgPSB1YmVyX3RyYWluKQp0cmFpbkNvdmFyaWF0ZXMgPC0gZGF0YS5mcmFtZShwcmVkaWN0KGRteSAsbmV3ZGF0YT1kYXRhLmZyYW1lKHNlYXNvbj11YmVyX3RyYWluJHNlYXNvbikpKQp0ZXN0Q292YXJpYXRlcyA8LSBkYXRhLmZyYW1lKHByZWRpY3QoZG15ICxuZXdkYXRhPWRhdGEuZnJhbWUoc2Vhc29uPXViZXJfdGVzdCRzZWFzb24pKSkKCiMgCm0zIDwtIHRzZ2xtKHRyYWluX3RzLCBtb2RlbD1saXN0KHBhc3Rfb2JzPTEsIHBhc3RfbWVhbj0xKSwgZGlzdHI9InBvaXNzb24iLCB4cmVnPXRyYWluQ292YXJpYXRlcykKCnloYXRfbTMgPC0gcHJlZGljdChtMywgbi5haGVhZD10ZXN0X3NpemUsIG5ld3hyZWc9dGVzdENvdmFyaWF0ZXMpCgpwcmVkaW50X20zIDwtIGRhdGEuZnJhbWUoeWhhdF9tMyRpbnRlcnZhbCkKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTMkbG93ZXIsIHltYXggPSBwcmVkaW50X20zJHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X20zJG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDEsMSkgYW5kIFdlZWtkYXkgRHVtbXkgVmFyaWFibGUiKQpgYGAKCmBgYHtyfQptNCA8LSB0c2dsbSh0cmFpbl90cywgbW9kZWw9bGlzdChwYXN0X29icz0xLCBwYXN0X21lYW49MSksIGRpc3RyPSJwb2lzc29uIikKCnloYXRfbTQgPC0gcHJlZGljdChtNCwgbi5haGVhZD10ZXN0X3NpemUpCgpwcmVkaW50X200IDwtIGRhdGEuZnJhbWUoeWhhdF9tNCRpbnRlcnZhbCkKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTQkbG93ZXIsIHltYXggPSBwcmVkaW50X200JHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X200JG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDEsMSkiKQpgYGAKCmBgYHtyfQoKbTUgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9MjgsIHBhc3RfbWVhbj0xKSwgZGlzdHI9InBvaXNzb24iKQoKeWhhdF9tNSA8LSBwcmVkaWN0KG01LCBuLmFoZWFkPXRlc3Rfc2l6ZSkKCnByZWRpbnRfbTUgPC0gZGF0YS5mcmFtZSh5aGF0X201JGludGVydmFsKQoKbWVhbigoeV90ZXN0IC0geWhhdF9tNSRtZWRpYW4pXjIpCgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT10cmFpbl90cykpKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeW1pbj1wcmVkaW50X201JGxvd2VyLCB5bWF4ID0gcHJlZGludF9tNSR1cHBlciksIGZpbGw9ImJsdWUiLCBhbHBoYT0wLjUpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodGVzdF90cyksIHk9eWhhdF9tNSRtZWRpYW4pLCBjb2xvcj0iYmx1ZSIpKwogIGdndGl0bGUoIlBvaXNzb24gR0xNIHdpdGggQVJNQSgyOCwxKSIpCgptMTMgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9MjgsIHBhc3RfbWVhbj0yOCksIGRpc3RyPSJwb2lzc29uIikKCnloYXRfbTYgPC0gcHJlZGljdChtMTMsIG4uYWhlYWQ9dGVzdF9zaXplKQoKcHJlZGludF9tNiA8LSBkYXRhLmZyYW1lKHloYXRfbTYkaW50ZXJ2YWwpCgptZWFuKCh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbileMikKCmdncGxvdCgpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodHJhaW5fdHMpLCB5PXRyYWluX3RzKSkrCiAgZ2VvbV9yaWJib24obWFwcGluZz1hZXMoeD10aW1lKHRlc3RfdHMpLCB5bWluPXByZWRpbnRfbTYkbG93ZXIsIHltYXggPSBwcmVkaW50X202JHVwcGVyKSwgZmlsbD0iYmx1ZSIsIGFscGhhPTAuNSkrCiAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeT15aGF0X202JG1lZGlhbiksIGNvbG9yPSJibHVlIikrCiAgZ2d0aXRsZSgiUG9pc3NvbiBHTE0gd2l0aCBBUk1BKDI4LDI4KSIpCmBgYAoKYGBge3J9CmRteSA8LSBkdW1teVZhcnMofnNlYXNvbiwgZGF0YSA9IHViZXJfdHJhaW4pCnRyYWluQ292YXJpYXRlcyA8LSBkYXRhLmZyYW1lKHByZWRpY3QoZG15ICxuZXdkYXRhPWRhdGEuZnJhbWUoc2Vhc29uPXViZXJfdHJhaW4kc2Vhc29uKSkpCnRlc3RDb3ZhcmlhdGVzIDwtIGRhdGEuZnJhbWUocHJlZGljdChkbXkgLG5ld2RhdGE9ZGF0YS5mcmFtZShzZWFzb249dWJlcl90ZXN0JHNlYXNvbikpKQoKbTYgPC0gdHNnbG0odHJhaW5fdHMsIG1vZGVsPWxpc3QocGFzdF9vYnM9NCwgcGFzdF9tZWFuPTEpLCBkaXN0cj0icG9pc3NvbiIsIHhyZWc9dHJhaW5Db3ZhcmlhdGVzKQpwcmVkaW50X202IDwtIGRhdGEuZnJhbWUoeWhhdF9tNiRpbnRlcnZhbCkKCnloYXRfbTYgPC0gcHJlZGljdChtNiwgbi5haGVhZD10ZXN0X3NpemUsIG5ld3hyZWc9dGVzdENvdmFyaWF0ZXMpCgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD10aW1lKHRyYWluX3RzKSwgeT10cmFpbl90cykpKwogIGdlb21fcmliYm9uKG1hcHBpbmc9YWVzKHg9dGltZSh0ZXN0X3RzKSwgeW1pbj1wcmVkaW50X202JGxvd2VyLCB5bWF4ID0gcHJlZGludF9tNiR1cHBlciksIGZpbGw9ImJsdWUiLCBhbHBoYT0wLjUpKwogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PXRpbWUodGVzdF90cyksIHk9eWhhdF9tNiRtZWRpYW4pLCBjb2xvcj0iYmx1ZSIpKwogIGdndGl0bGUoIlBvaXNzb24gR0xNIEFSTUEoNCwxKSBhbmQgV2Vla2RheSBEdW1teSBWYXJpYWJsZSIpCmBgYAoKClRoYXQgd29ya3MsIHNob3VsZCBJIGluY2x1ZGUgdGhlIEFJQyAvIEJJQyB0YWJsZXMgdXAgaGVyZSB0b28/IEFuZCB0aGUgcmVzaWR1YWxzIHBsb3RzIGFuZCBzdHVmZj8gQWxyaWdodHksIGlmIHlvdSBjb3VsZCBqdXN0IHB1dCBhbiBleGFtcGxlIHNlY3Rpb24gd2l0aCB5b3VyIG1vZGVsIGFuZCBJIGNhbiBqdXN0IGNvcHkgdGhlIHNhbWUgZm9ybWF0LiBJIGhhdmUgcGxlbnR5IG9mIEJTIEkgY2FuIHN0aWNrIGluIGQ6CgpKayBpbSBnb25uYSBkZWxldGUgdGhpcyBzbyB3ZSBoYXZlIHNwYWNlIGhhaGEgYnV0IEknbGwgY29weSBpdCBpbiBhIHNlcGFyYXRlIHRleGN0IGZpbGUKQ291bGQgeW91IGluY2x1ZGUgYSBxdWljayB3cml0ZXVwIG9uIGl0PyBJIHdhcyBnb25uYSB3cml0ZSB1cCBzb21ldGhpbmcgcXVpY2sgYWJvdXQgU0FSSU1BLCBhbmQgV2lsbGlhbSBpcyBnb25uYSB3cml0ZSB1cCBzb21ldGhpbmcgYWJvdXQgUG9pc3Nvbi4gU291bmRzIGdvb2QsIHdvdWxkIHlvdSBiZSBkb3duIHRvIHB1dCBpdCB1cCBoZXJlPyBXZSB3ZXJlIHRyeWluZyB0byB0aGluayBvZiB3aGF0IHRvIHB1dCB1cCBpbiB0aGlzIHNlY3Rpb24gKHVubGVzcyB5b3UgaGF2ZSBhbiBpZGVhIG9mIHdoYXQgdG8gcHV0IC4gU291bmRzIGdvb2QKCgpPdXIgYW5hbHlzaXMgY29uc2lkZXJlZCB0aHJlZSBtb2RlbHM6XFwKdGhhdHMgcmlnaHQsIGFuZCBtdWx0aXZhcmlhdGUgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxzLCBvciBWR0FNJ3MuU3VyZSwgaWxsIGRvIGEgd3JpdGUgdXAgaW4gdGhlIG1vZGVsIGZpdHRpbmcgc2VjdGlvbiwgaWxsIHR5cGUgb3V0IHRoZSBhY3R1YWwgbW9kZWxzIG1hdGhlbWF0aWNhbGx5IGFuZCB0cnkgdG8gZXhwbGFpbiB0aGVtLiB5ZWEgaSBjYW4gd3JpdGUgaXQgaGVyZSBpbnN0ZWFkLCBmb3IgbW9kZWwgZml0dGluZyBhbmQgdmFsaWRhdGlvbiBwdXQgcGxvdHMgb2YgeW91ciBmb3JlY2FzdCBiZXNpZGUgdGhlIHZhbGlkYXRpb24gYW5kIHRhbGsgYWJvdXQgaG93IHdlbGwgdGhleSBmaXQgdGhlIGRhdGEuIGFsc28gcHJvYmFibHkgaW5jbHVkZSB0YWJsZXMgd2l0aCB0aGUgbW9kZWxzIGFuZCBlcnJvcnMgb24gdGhlIHZhbGlkYXRpb24gc2V0LiBZZXMsIGFuZCB0aGUgbGp1bmcgYm94IHN0dWZmIHRvbywgdGhlIHByb2NlZHVyZXMgeW91IHVzZWQgdG8gY2hvb3NlIHRoZSBiZXN0IG1vZGVsIHNob3VsZCBiZSB0aGVyZSBsaWtlIHRoZSBhaWMsIGJ1dCBsZXRzIG5vdCBnbyB0b28gY3JhenkgbG9sLiBJIHRoaW5rIGF0IG1vc3Qgd2UgaW5jbHVkZSB2YWxpZGF0aW9uIGVycm9yLCBhaWMsIHJlc2lkdWFsIG9yIFEtUSBwbG90cywgYW5kIGxqdW5nIGJveCBzaG91bGQgYmUgZ29vZC4gZm9yIHN1cmUsIGFscmlnaHQgaGF2ZSBmdW4gbG9sCgpgYGB7cn0KdHJhaW5fZnJlcSA8LSB0cyh1YmVyX3RyYWluJEVhc3RfVmlsbGFnZSwgZnJlcXVlbmN5PTI4KQp0ZXN0X2ZyZXEgPC0gdHModWJlcl90ZXN0JEVhc3RfVmlsbGFnZSwgZnJlcXVlbmN5PTI4KQoKdHJhaW5fZnJlcSAlPiUgYXV0by5hcmltYSgpIC0+IG0xMAoKcDEgPC0gbTEwICU+JSBmb3JlY2FzdChoPTEyMCkgJT4lIGF1dG9wbG90KCkKcDIgPC0gbTEwICU+JSByZXNpZHVhbHMoKSAlPiUgZ2dBY2YoKSArIGdndGl0bGUoIkFDRiBvZiBSZXNpZHVhbHMiKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0xLCBuY29sPTIpCmBgYAoKYGBge3J9CmNoZWNrcmVzaWR1YWxzKG0xMCkKYGBgCgpgYGB7cn0KbmFpdmVzX20gPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDAsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwwKSwgcGVyaW9kPTI4KSkKCnNmaXQgPC0gdHJhaW5fdHMgJT4lIHNuYWl2ZShtPTI4KSAKcDEgPC0gbmFpdmVzX20gJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoUEk9RkFMU0UpCnAyIDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKSAlPiUgYXV0b3Bsb3QoKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdz0yLCBuY29sPTEpCmBgYAoKYGBge3J9CnNmaXQgPC0gc25haXZlKHRyYWluX3RzKSAKeWhhdF9zZml0IDwtIHNmaXQgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQpzZml0ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkgJT4lIGF1dG9wbG90KCkKYGBgCgojIyBBY2N1cmFjeSBNZWFzdXJlcwoKYGBge3J9CiMgU0FSSU1BIG1vZGVscwptMSA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoMSwgMSwgMCksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMCwxLDEpLCBwZXJpb2Q9MjgpKQoKbTIgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDQsIDEsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwxKSwgcGVyaW9kPTI4KSkKCm0zIDwtIEFyaW1hKHRyYWluX3RzLCAKICAgICAgb3JkZXI9Yyg0LCAxLCAwKSwgCiAgICAgIHNlYXNvbmFsPWxpc3Qob3JkZXI9YygwLDEsNCksIHBlcmlvZD0yOCkpCgptNCA8LSBBcmltYSh0cmFpbl90cywgCiAgICAgIG9yZGVyPWMoMywgMCwgMiksIAogICAgICBzZWFzb25hbD1saXN0KG9yZGVyPWMoMiwxLDApLCBwZXJpb2Q9MTMpKQoKbTUgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDQsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDIsMSwwKSwgcGVyaW9kPTI4KSkKCnNfbmFpdmUgPC0gQXJpbWEodHJhaW5fdHMsIAogICAgICBvcmRlcj1jKDAsIDAsIDApLCAKICAgICAgc2Vhc29uYWw9bGlzdChvcmRlcj1jKDAsMSwwKSwgcGVyaW9kPTI4KSkKCgp5aGF0X20xIDwtIG0xICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKeWhhdF9tMiA8LSBtMiAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCnloYXRfbTMgPC0gbTMgJT4lIGZvcmVjYXN0KGg9dGVzdF9zaXplKQp5aGF0X200IDwtIG00ICU+JSBmb3JlY2FzdChoPXRlc3Rfc2l6ZSkKeWhhdF9tNSA8LSBtNSAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCnloYXRfbmFpdmUgPC0gc19uYWl2ZSAlPiUgZm9yZWNhc3QoaD10ZXN0X3NpemUpCmBgYAoKCmBgYHtyfQphY2N1cmFjeSh5aGF0X20xLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X20yLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X20zLCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X200LCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X201LCB0ZXN0X3RzKQphY2N1cmFjeSh5aGF0X25haXZlLCB0ZXN0X3RzKQpgYGAKCmBgYHtyfQojIAptc2VfdHNnbG0xIDwtIG1lYW4oKHlfdGVzdCAtIHloYXRfbTUkbWVkaWFuKV4yKQptc2VfdHNnbG0yOCA8LSBtZWFuKCh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbileMikKCm1hZV90c2dsbTEgPC0gbWVhbihhYnMoeV90ZXN0IC0geWhhdF9tNSRtZWRpYW4pKQptYWVfdHNnbG0yOCA8LSBtZWFuKGFicyh5X3Rlc3QgLSB5aGF0X202JG1lZGlhbikpCgptc2VfdHNnbG0xCm1zZV90c2dsbTI4Cm1hZV90c2dsbTEKbWFlX3RzZ2xtMjgKCmBgYAoKYGBge3J9CgoKCmBgYAoK